package com.learn.thread;

import com.learn.thread.exception.ExceptionHandler;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args) {

        // threadState();
        // threadInterrupt();
        // threadDaemon();
        // threadException();
        // threadUnSafe();
        // threadGroup();
        // threadGroupException();
        threadFactory();

    }

    //===================================================================================

    private static void threadFactory() {
        MyThreadFactory myThreadFactory = new MyThreadFactory("MyThreadFactory");
        Thread thread;
        System.out.printf("Starting the threads：\n");
        for (int i = 0; i < 10; i++) {
            thread = myThreadFactory.newThread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
        System.out.printf("ThreadFactory stats：\n");
        System.out.printf("%s\n", myThreadFactory.getStats());
    }

    private static void threadGroupException() {
        MyThreadGroup myThreadGroup = new MyThreadGroup("MyThreadGroup");
        Task task = new Task();
        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(myThreadGroup, task);
            thread.start();
        }
    }

    private static void threadGroup() {
        ThreadGroup threadGroup = new ThreadGroup("Searcher");
        Result result = new Result();
        SearchTask searchTask = new SearchTask(result);
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(threadGroup, searchTask);
            thread.start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //list()打印线程组对象的信息
        System.out.printf("Number of Threads：%d\n", threadGroup.activeCount());
        System.out.printf("Information about the ThreadGroup：\n");
        threadGroup.list();

        //activeCount()获取线程组包含的线程数目
        //enumerate()获取线程组包含的线程列表

        Thread[] threads = new Thread[threadGroup.activeCount()];
        threadGroup.enumerate(threads);
        for (int i = 0; i < threadGroup.activeCount(); i++) {
            System.out.printf("Thread %s：%s\n", threads[i].getName(), threads[i].getState());
        }

        //等到线程组的第一个线程结束
        waitFinish(threadGroup);

        //中断组内的其它线程
        threadGroup.interrupt();
    }

    private static void waitFinish(ThreadGroup threadGroup) {
        while (threadGroup.activeCount() > 9) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void threadUnSafe() {
        // UnSafeTask task = new UnSafeTask();
        SafeTask task = new SafeTask();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(task);
            thread.start();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void threadException() {
        Thread thread = new Thread(new ExceptionTask());
        thread.setUncaughtExceptionHandler(new ExceptionHandler());
        thread.start();
    }

    private static void threadDaemon() {
        Deque<Event> deque = new ArrayDeque<>();
        WriteTask writeTask = new WriteTask(deque);
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(writeTask);
            thread.start();
        }

        CleanerTask cleanerTask = new CleanerTask(deque);
        cleanerTask.start();
    }

    private static void threadInterrupt() {
        Thread task = new PrimeGenerator();
        task.start();

        try {
            Thread.sleep(1000); //主线程睡几秒钟
            System.out.printf("Main Thread State：%s \n", Thread.currentThread().getState());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (false) {
            Thread.interrupted();
        }

        task.interrupt();
    }

    private static void threadState() {
        Thread[] threadArr = new Thread[10];
        Thread.State[] stateArr = new Thread.State[10];

        for (int i = 0; i < 10; i++) {
            threadArr[i] = new Thread(new Calculator(i));
            if (i % 2 == 0) {
                threadArr[i].setPriority(Thread.MAX_PRIORITY);
            } else {
                threadArr[i].setPriority(Thread.MIN_PRIORITY);
            }
            threadArr[i].setName("Thread => " + i);
        }

        try (FileWriter file = new FileWriter(".\\log.txt"); PrintWriter pw = new PrintWriter(file)) {
            for (int i = 0; i < 10; i++) {
                pw.println("Main：Status of Thread 【" + i + "】：" + threadArr[i].getState());
                stateArr[i] = threadArr[i].getState();
            }

            pw.printf("Main：************************************************ \n");

            for (int i = 0; i < 10; i++) {
                threadArr[i].start();
            }

            boolean finish = false;
            while (!finish) {
                for (int i = 0; i < 10; i++) {
                    if (threadArr[i].getState() != stateArr[i]) {
                        writeThreadInfo(pw, threadArr[i], stateArr[i]);
                        stateArr[i] = threadArr[i].getState();
                    }
                }

                finish = true;

                for (int i = 0; i < 10; i++) {
                    finish = finish && (threadArr[i].getState() == Thread.State.TERMINATED);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void writeThreadInfo(PrintWriter pw, Thread thread, Thread.State state) {
        pw.printf("Main：Id => %d - %s \n", thread.getId(), thread.getName());
        pw.printf("Main：Priority => %d \n", thread.getPriority());
        pw.printf("Main：Old State => %s \n", state);
        pw.printf("Main：New State => %s \n", thread.getState());
        pw.printf("Main：************************************************ \n");
    }

}
